package edu.jhu.cs.oose.scheduleit.util;

import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
/**
 * Class for parsing the given query/browse conditions and converting it to a DetachedCriteria object. 
 * @author oose_group2
 *
 */
public class FilterParser {
	
	/**
	 * Convert the given query/browse conditions into Restrictions used by a DetachedCriteria object.
	 * @param criteria The DetachedCriteria used to 
	 * @param column The column name for query
	 * @param comparator The comparator, for example, EQ(equal). to compare.
	 * @param value The value for comparison
	 * @param ascending  true means showing the result ascending, false means showing the result descending.
	 */
	@SuppressWarnings("unchecked")
	public static DetachedCriteria parse(Class targetClass,List<Filter> filters){
		DetachedCriteria criteria = DetachedCriteria.forClass(targetClass);	
		Criterion root = null;
		for (Filter filter: filters){
			if (filter.getValue() != null){
				Criterion temp = null;
				switch (filter.getComparator()){
					case EQ:
						temp = Restrictions.eq(filter.getAttributeKind().toString(),filter.getValue());
						break;
					case NE:
						temp = Restrictions.ne(filter.getAttributeKind().toString(),filter.getValue());
						break;
					case GT:
						temp = Restrictions.gt(filter.getAttributeKind().toString(),filter.getValue());
						break;
					case GE:
						temp = Restrictions.ge(filter.getAttributeKind().toString(),filter.getValue());
						break;
					case LT:
						temp = Restrictions.lt(filter.getAttributeKind().toString(),filter.getValue());
						break;
					case LE:
						temp = Restrictions.le(filter.getAttributeKind().toString(),filter.getValue());
						break;
					case LIKE:
						temp = Restrictions.ilike(filter.getAttributeKind().toString(),genreateLikeString(filter.getValue()));
						break;
				}
				if (root == null){
					root = temp;
				}else{
					if (filter.getOptional()){
						root = Restrictions.or(root, temp);
					}else{
						root = Restrictions.and(root,temp);
					}
				}
			}
			if (filter.getAscending() != null){
				if (filter.getAscending()){
					criteria.addOrder(Order.asc(filter.getAttributeKind().toString()));
				}else{
					criteria.addOrder(Order.desc(filter.getAttributeKind().toString()));
				}
			}
		}
		if (root != null){
			criteria.add(root);
		}
		return criteria;
		
	}
	/**
	 * Convert a String or a list of Strings to a suitable value for HQL String comparison
	 * @param value A String or a list of Strings for comparison
	 * @return  The String for HQL String comparison
	 */
	@SuppressWarnings("unchecked")
	private static String genreateLikeString(Object value){
		StringBuilder sb = new StringBuilder();
		if (value instanceof Collection<?>){
			Collection<String> tags = (Collection<String>) value;	
			for (String tag:tags){
				sb.append("% ");
				sb.append(tag);
				sb.append(" ");
			}
			sb.append("%");
		}else{
			sb.append("% ");
			sb.append(value);
			sb.append(" %");
		}
		return sb.toString();
	}
	/**
	 * Translate a list of tags into a single string which is used to store at the database
	 * @param tags The list of tags which will be saved into the database.
	 * @return A string representing whole tags in the list.
	 */
	public static String translateBeforeTagWrite(List<String> tags){
		
		if (tags != null && tags.size() >0){
			StringBuilder sb = new StringBuilder();
			sb.append(" ");
			sb.append(tags.get(0));
			for (int i = 1; i < tags.size(); i++){
				sb.append("  ");
				sb.append(tags.get(i));
			}
			//
			for (int i = 1; i < tags.size(); i++){
				if (i%2 == 1){
					for (int j = tags.size()-2; j > -1; j--){
						sb.append("  ");
						sb.append(tags.get(j));
					}
				}else{
					for (int j = 1; j < tags.size(); j++){
						sb.append("  ");
						sb.append(tags.get(j));
					}
				}
			}
			sb.append(" ");
			return sb.toString();
		}else{
			return "";
		}
	}
	/**
	 * Translate a string into a list of tags.
	 * @param tags The string representing a list of tags
	 * @return The list of tags 
	 */
	public static List<String> translateBeforeTagRead(String tags){
		List<String> result = new LinkedList<String>();
		if (tags != null && tags.length() > 0){
			String[] temp = tags.trim().split("  ");
			System.out.println("Get in "+tags+", "+temp.length);
			int size = 1;
			while (true){
				if (temp.length == size + (size-1)* (size-1)){
					break;
				}
				size++;
			}
			for (int i = 0; i < size; i++){
				result.add(temp[i]);
			}
		}
		return result;
	}
	/**
	 * Translate the database-saved topic into the readable topic
	 * @param topic The topic saved in the database.
	 * @return The topic which is readable by the end-user.
	 */
	public static String translateBeforeTopicRead(String topic){
		List<String> tmp = translateBeforeTagRead(topic);
		if (tmp.size() == 0){
			return "<<Empty>>";
		}else{
			StringBuilder sb = new StringBuilder();
			for(String s:tmp){
				sb.append(s);
				sb.append(" ");
			}
			return sb.toString().trim();
		}
	}
	/**
	 * Translate the user given topic into the database-save topic
	 * @param topic The topic given by user
	 * @return The topic used to save in the database.
	 */
	public static String translateBeforeTopicWrite(String topic){
		if (topic != null && topic.length() > 0){
			List<String> tmp = new LinkedList<String>();
			String[] splitedTmp = topic.split(" ");
			for (String s:splitedTmp){
				tmp.add(s);
			}
			return translateBeforeTagWrite(tmp);
		}else{
			return "";
		}
	}
}
